package ru.dcsearch.indexer;
/***************************************************************************
 *   Modified by TMC
 *   berechitai@gmail.com
 *
 *   This file is part of DC Indexer project
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 ***************************************************************************/
/*
 *  Mobile DC - a DirectConnect client written in Java and designed for
 *  the Sony Ericsson p800/p900 or equivalent Symbian OS 7.0 platforms
 *  Copyright (C) 2004 Conrad Helgesson, vakum@home.se
 *
 *  This file is part of Mobile DC.
 *
 *  Mobile DC is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  Mobile DC is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Mobile DC; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

import java.io.RandomAccessFile;
import java.io.File;
import java.io.IOException;
import java.io.BufferedInputStream;
import java.net.*;

public class ClientConnection extends Thread
{
	//::DCEncryptionHandler
	private DCEncryptionHandler encryptionHandler = new DCEncryptionHandler();

	//::statics
	private final static char				COMMAND_END_CHAR='|';
	private final static char				COMMAND_DOL_CHAR='$';
	private final static char				COMMAND_SEP_CHAR=' ';
	private final static char				DIRPATH_SEP_CHAR='\\';
	private final static String				MediaPath = "D:\\Media files\\";

	//::blebb
	private ClientConnectionManager			CCM;
	private DCClient							mw;
	private int								ccID;
	private boolean							isSocketServer;
	private boolean							isMDCuser;

	//::Net-File-things
	private Socket							sock;
    private ServerSocket					serv_sock;
    private TokenInputStream				indata;
    private ExtendedBufferedOutputStream	outdata = null;
    private RandomAccessFile				RAFile;
    private File							file;
    private int                             port;

    private int verifyResumeSize;
    private String thekey;
    private String thelock;
    private String filename;
    private String filen;
	private long					fileLength, bytesReceived, resumeFrom;

    //Features
    private boolean supportsAdcGet = false;

	private boolean toStop = false;
	
	
	//State codes
	final static int STATE_DEFAULT = 0;
	final static int STATE_PROCESSING = 1;
	final static int STATE_SUCCESSED = 2;
	final static int STATE_NOSLOTS = 3;
	final static int STATE_TIMEOUT = 4;
	final static int STATE_LOWSPEED = 5;
	final static int STATE_OTHER_EXCEPTION = 6;

	//::Main
	public ClientConnection(ClientConnectionManager ccm, DCClient m, int id, boolean isSS, int port)
	{
		CCM = ccm;
		mw = m;
		ccID = id;
		isSocketServer = isSS;
		isMDCuser = false;
		filen = "";
        this.port = port;
		new Thread(this).start();
	}

	//::Thread
	public void run()
	{
		toStop = false;
        int timeout;
        try {
			//Active mode
			if(isSocketServer) {
                Logger.printP2PLog("PORT: "+ port);
                
				serv_sock = new ServerSocket();
                serv_sock.setReuseAddress(true);
                int test = 10;
                do {
                  try {
                    serv_sock.bind(new InetSocketAddress(port));
                    test = 0;
                  } catch (java.net.BindException e) {

                      Logger.printP2PLog("Address already in use. Wait");
                      Thread.sleep(1000);
                      test--;

                  }
                } while (test!=0);
                serv_sock.setSoTimeout(15000);
				sock = serv_sock.accept();
				serv_sock.close();
				serv_sock = null;
					//Logger.printLog ("CLIENT Running: I was server");
			} else {
				sock = new Socket(CCM.getIP(ccID),port);
				Logger.printLog ("CLIENT Running: I wasn't server");
			}
            indata = new TokenInputStream(new BufferedInputStream(sock.getInputStream()), COMMAND_END_CHAR);
            outdata = new ExtendedBufferedOutputStream(sock.getOutputStream(), 64000);

			if(!isSocketServer) {
				SendCmd("$MyNick", mw.p.getMynick());
				SendCmd("$Lock", "EXTENDEDPROTOCOLABCABCABCABCABCABC Pk=indexer"+DCClient.version+"ABCABC");
			}

			CCM.setState(ccID, ClientConnectionManager.C_ing);
			timeout = 0;
		} catch (Exception e) {

			Logger.printLog("CLIENT: "+e);
			CCM.setAbort(ccID, "Connection Timeout");
			setUserCompleteState(STATE_TIMEOUT);
			disconnect();
            return;
		}

		processInput(timeout);
	}

	private void processInput(int timeout) {
		try {
			while(sock != null) {
				if (toStop) return;
				if(CCM.getState(ccID) == ClientConnectionManager.C_ing || CCM.getState(ccID) == ClientConnectionManager.ULoad || CCM.getState(ccID) == ClientConnectionManager.Waita || CCM.getState(ccID) == ClientConnectionManager.DLoad) {
					//::Protocol
					//Logger.printLog ("CLIENT: protocol?");
                    String fromClient = indata.readToken();
					if(fromClient != null) {
						timeout = 0;
						ProcessCmd(fromClient);
					} else {
						timeout += 1;
						try {sleep(1000);} catch(InterruptedException e) { }
						if(timeout == 20) {
							if(CCM.getState(ccID) == ClientConnectionManager.C_ing) {
								CCM.setAbort(ccID, "Connection Timeout");
								disconnect();
                                return;
							}
						} else if(timeout >= 30) {
							if(!(CCM.getState(ccID) == ClientConnectionManager.Waita)) {
								CCM.setAbort(ccID, "Connection Timeout");
							} else {
								CCM.setState(ccID, ClientConnectionManager.Finit);
								CCM.UpdateStatus(ccID);
							}
							disconnect();
                            return;
						}
					}
				}

				else if(CCM.getState(ccID) == ClientConnectionManager.ULing) {
					//::Uploading
					/*while(bytesReceived != fileLength) {
						int size = (int) Math.min(outdata.nonBlockingCapacity(), fileLength - bytesReceived);
						if (size > 0) {
							byte[] buffer = new byte[size];
							RAFile.read(buffer);
							outdata.write(buffer);
							bytesReceived += size;
							CCM.setBytes(ccID, bytesReceived);
							mw.ULtraffic += size;
						} else sleep(50);
					}

					outdata.flush();
					Logger.printLog("CLIENT: transfer done...");
					RAFile.close();
					RAFile = null;
					CCM.setState(ccID, CCM.Waita);
					CCM.UpdateStatus(ccID);
					//try flush again?, i seem to have a problem with uploading to dc++ users..
					outdata.flush();
				*/
                    disconnect();
				}

				else if(CCM.getState(ccID) == ClientConnectionManager.RSing) {
                    if (downloadResumeFile()) return;

                }

				else if(CCM.getState(ccID) == ClientConnectionManager.DLing) {
					//::Downloading
                    downloadFile();
				}

				else if(CCM.getState(ccID) == ClientConnectionManager.Not_C) {
					Logger.printLog("Impossible thing happened, cooool");
					disconnect();
                    return;
				} 
				
				else if (CCM.getState(ccID)== ClientConnectionManager.Finit) {
					
					disconnect();
				}
				
				
				// End state == ?
			} //whiletrue

		} catch(Exception e) {
			Logger.printLog("CLIENT ROOT ERROR: "+e);
			if(CCM.getState(ccID) != ClientConnectionManager.Not_C) {
				CCM.setAbort(ccID, "Disconnected");
				disconnect();
            }
		}
	}

    private boolean downloadResumeFile() throws IOException, InterruptedException {
        //::Resume Download
        //Not suported yet
        Logger.printLog("CLIENTRES: Resume...");

        if(indata.available() >= verifyResumeSize) {
            byte[] buffer = new byte[verifyResumeSize];
            int x1 = indata.read(buffer);
            byte[] fileBlock = new byte[verifyResumeSize];
            int x2 = RAFile.read(fileBlock);

            if(!(new String(buffer)).equals((new String(fileBlock)))) {
                Logger.printLog("CLIENTRES: Tried Resume, but the files weren't equal.");
                CCM.setAbort(ccID, "Rollback inconsistency");
                disconnect();
                return true;
            } else {
                if (bytesReceived == fileLength) {
                    RAFile.close();
                    RAFile = null;
                    filename = null;
                    file = null;
                    CCM.setState(ccID, ClientConnectionManager.Finit);
                    CCM.UpdateStatus(ccID);
                    //if(filen.endsWith("MyList.DcLst") || filen.endsWith("files.xml.bz2"))
                    //	mw.timer.FileListDLed(filen);
                } else {
                		CCM.setState(ccID, ClientConnectionManager.DLing);
                		RAFile.seek(resumeFrom);
                		RAFile.write(buffer);
                		Logger.printDownloadlProcess(bytesReceived);
                }
            }
        } else sleep(50);

        disconnect();
        return false;
    }

    private void downloadFile() {
        
    	try {
	    	int timeoutCount = 0;
	    	while(bytesReceived != fileLength) {
	            int size = (int) Math.min(indata.available(), fileLength - bytesReceived);
	            if (size > 0) {
	            	timeoutCount = 0;
	                byte[] buffer = new byte[size];
	                indata.read(buffer);
	                RAFile.write(buffer);
	                bytesReceived += size;
	                CCM.setBytes(ccID, bytesReceived);
	                mw.DLtraffic += size;
	                
	                Logger.printDownloadlProcess(bytesReceived);
	            } else {
	            	timeoutCount++;
	            	if (timeoutCount > 500) {
	            		
	            		throw new IOException("Download timeout");
	            	}
	            	sleep(50);
	            	}
	           
	        }
	
	        Logger.printLog("CLIENT: transfer done...");
	        
	        CCM.setState(ccID, ClientConnectionManager.Finit);
	        CCM.UpdateStatus(ccID);
	        //if(filen.endsWith("MyList.DcLst") || filen.endsWith("files.xml.bz2"))
	        //	mw.timer.FileListDLed(filen);
    	}
	        catch (Exception e) {
	        	
	        	Logger.printLog("CLIENT: "+e);
				CCM.setAbort(ccID, "Low download speed");
				setUserCompleteState(STATE_LOWSPEED);
				disconnect();
	            return;
	        	
	        
	        }
    	}


    //::Process an incoming command
	private void ProcessCmd(String in) throws IOException
	{
        String inccmd="",incdat="";
		Logger.printP2PLog("CLIENT IN: "+in);

		mw.DLtraffic += in.length()+1;

		int sepindex = in.indexOf(COMMAND_SEP_CHAR);

		if(sepindex < 0)
			inccmd = in;
		else {
			inccmd=in.substring(0, sepindex);
			incdat=in.substring(sepindex+1, in.length());
		}

		//::Next first thing to be recieved from client.
		if ("$Lock".equals(inccmd)) {
            cmdLock(in, incdat);

		//::Upload thingy
		} else if ("$Get".equals(inccmd)) {
            cmdGet(incdat);

		//::Remote client tells us direction
		} else if ("$Direction".equals(inccmd)) {
            cmdDirection(incdat);

		//::After Key we can ask to download something, if that's the direction
		} else if ("$Key".equals(inccmd)) {
            cmdKey();

            //::No slots available
		} else if ("$MaxedOut".equals(inccmd)) {
            Logger.printGlobalLog("User " + CCM.getNick(ccID)+ " has no free slots.");
			CCM.setAbort(ccID, "No Slots Available");
			if (file.length() == 0) file.delete(); //else we get lots of 0byte files
			setUserCompleteState(STATE_NOSLOTS);
			disconnect();
            return;

		//::Handshake done, start Upload
		} else if ("$Send".equals(inccmd)) {
			CCM.UpdateStatus(ccID);
			CCM.setState(ccID, ClientConnectionManager.ULing);

		//::First data received, the Nickname
		} else if ("$MyNick".equals(inccmd)) {
			if(CCM.getNick(ccID).equals(""))
				CCM.setNick(ccID, incdat);
            Logger.printGlobalLog("User connected: "+incdat);

		//::An error occured remotely
		} else if ("$Error".equals(inccmd)) {
			CCM.setAbort(ccID, incdat);
			disconnect();
            return;

		//::The last info needed to start Download a remote file
		} else if ("$FileLength".equals(inccmd)) {
            if (cmdFileLength(incdat)) return;

            //::A list of supported features of the other client
		} else if ("$Supports".equals(inccmd)) {
            if (incdat.contains("ADCGet")) {

                supportsAdcGet = true;

            }

		//::New style upload
		} else if ("$UGetBlock".equals(inccmd)) {
            if (cmdUserGetBlock(incdat)) return;

            //::Get Filelistfilelength in bytes
		} else if ("$GetListLen".equals(inccmd)) {
			SendCmd("$ListLen", "0");

		//::To cancel a transfer, this is not possible to get during transfer though :p  me be bad coder ugha ugha
		} else if ("$Cancel".equals(inccmd)) {
			SendCmd("$Canceled","");
			CCM.setAbort(ccID, "Disconnected");
			disconnect();
            return;

		//::If we $Cancel a transfer, this is the response.
		} else if ("$Canceled".equals(inccmd)) {

        } else if ("$ADCSND".equals(inccmd)) {   

            if (cmdAdcSnd(incdat)) return;

        } else {
		}
		incdat = null;
		inccmd = null;
	}

    private boolean cmdUserGetBlock(String incdat) throws IOException {
        int sepindex;
        if(Integer.parseInt(mw.p.getUlSlots()) < CCM.slotstaken) {
            SendCmd("$MaxedOut","");
            CCM.setAbort(ccID, "No Free Slots to User");
            disconnect();
            return true;
        } else {
            String one = incdat.substring(0, incdat.indexOf(COMMAND_SEP_CHAR));
            String two = incdat.substring(one.length()+1, incdat.indexOf(COMMAND_SEP_CHAR, one.length()+2));

            sepindex = incdat.indexOf(DIRPATH_SEP_CHAR);
            if(sepindex < 0) {
                filename = incdat.substring(one.length()+two.length()+2,incdat.length());
                filen = filename;
            } else {
                filename = incdat.substring(sepindex+1, incdat.length());
                sepindex = filename.indexOf(DIRPATH_SEP_CHAR);
                if(sepindex < 0)	filen = filename;
                else				filen = filename.substring(filename.lastIndexOf(DIRPATH_SEP_CHAR)+1);
            }

            CCM.setFile(ccID, filename);

            long fileoffset = Long.parseLong(one);
            if(filename.equals("MyList.DcLst") || filename.equals("files.xml.bz2"))
                file = new File(filename);
            else file = new File("./",filename);

            if(!file.exists()) {
                SendCmd("$Error", "File not available.");
                CCM.setAbort(ccID, "File Not Available to User");
                disconnect();
                return true;
            }

            //if(mw.debug) System.out.println("CLIENT PATH: "+file.getPath());
            RAFile = new RandomAccessFile(file, "rw");
            RAFile.seek(fileoffset);
            bytesReceived = fileoffset;
            CCM.setBytes(ccID, bytesReceived);
            fileLength = Long.parseLong(one)+Long.parseLong(two);
            CCM.setMaxBytes(ccID, fileLength);

            CCM.UpdateStatus(ccID);
            CCM.setState(ccID, ClientConnectionManager.ULing);
            SendCmd("$Sending", two);
        }
        return false;
    }

    private boolean cmdFileLength(String incdat) throws IOException {
        fileLength = Long.parseLong(incdat);
        CCM.setMaxBytes(ccID, fileLength);
        if (file.length() == fileLength) {
            CCM.setAbort(ccID, "File Already Exists");
            disconnect();
            return true;
        } else if(0 < verifyResumeSize) {
            CCM.setState(ccID, ClientConnectionManager.RSing);
            CCM.UpdateStatus(ccID);
        } else if(file.length() == 0) {
            CCM.UpdateStatus(ccID);
            CCM.setState(ccID, ClientConnectionManager.DLing);
        } else {
            CCM.setAbort(ccID, "Connection Closed");
            disconnect();
            return true;
        }
        SendCmd("$Send","");
        return false;
    }

    private boolean cmdAdcSnd(String incdat) {


         //Find $ADCSND file files.xml.bz2 0 8775 ZL1|<file contents>
            //Before filename
            /*int i = incdat.indexOf(' ');
            String tempstr = incdat.substring(i+1);
            //Before zero
            i = tempstr.indexOf(' ');
            tempstr = tempstr.substring(i+1);
            //Before filename
            i = tempstr.indexOf(' ');
            tempstr = tempstr.substring(i+1);

            //After filename
            i = tempstr.indexOf(' ');
            fileLength = Long.parseLong(tempstr.substring(0,i));
            */
            int i = incdat.lastIndexOf(' ');
            fileLength = Long.parseLong(incdat.substring(i+1));

            CCM.setMaxBytes(ccID, fileLength);
            if (file.length() == fileLength) {
                CCM.setAbort(ccID, "File Already Exists");
                disconnect();
                return true;
            } else if(0 < verifyResumeSize) {
                CCM.setState(ccID, ClientConnectionManager.RSing);
                CCM.UpdateStatus(ccID);
            } else if(file.length() == 0) {
                CCM.UpdateStatus(ccID);
                CCM.setState(ccID, ClientConnectionManager.DLing);
            } else {
                CCM.setAbort(ccID, "Connection Closed");
                disconnect();
                return true;
            }
            Logger.printGlobalLog("Filesize : "+ fileLength);
            return false;

    }
    private void cmdKey() throws IOException {
        int sepindex;
        if(CCM.getDirection(ccID) == ClientConnectionManager.Download) {
            resumeFrom = 0;
            if(filename == null || filename.equals("")) {
                filename = CCM.getFile(ccID);
                }

            sepindex = filename.indexOf(DIRPATH_SEP_CHAR);
            if(sepindex < 0) filen = filename;
            else filen = filename.substring(filename.lastIndexOf(DIRPATH_SEP_CHAR)+1);

            File downdir;
            if(filen.equals("MyList.DcLst") || filen.equals("files.xml.bz2")) {
                if(filename.equals("MyList.DcLst") && isMDCuser) {
                    filen = CCM.getNick(ccID)+".files.xml.bz2";
                    filename = "files.xml.bz2";
                } else
                    filen = CCM.getNick(ccID)+filen;
                downdir = new File("FileList");
                file = new File("FileList",filen);
                if (file.exists()) file.delete();

            } else {
                if(mw.p.IsEnabled("tdldir")) {
                    if(filen.endsWith(".jpg") || filen.endsWith(".bmp") || filen.endsWith(".gif") || filen.endsWith(".jpeg") || filen.endsWith(".mbm") || filen.endsWith(".wbmp")) {
                        downdir = new File(MediaPath+"image\\Received");
                        file = new File(MediaPath+"image\\Received",filen);
                    } else if(filen.endsWith(".wav") || filen.endsWith(".amr") || filen.endsWith(".au") || filen.endsWith(".mp3") || filen.endsWith(".mid")) {
                        downdir = new File(MediaPath+"audio\\Received");
                        file = new File(MediaPath+"audio\\Received",filen);
                    } else if(filen.endsWith(".mp4") || filen.endsWith(".avi") || filen.endsWith(".mpg")) {
                        downdir = new File(MediaPath+"video\\Received");
                        file = new File(MediaPath+"video\\Received",filen);
                    } else if(filen.endsWith(".doc") || filen.endsWith(".txt") || filen.endsWith(".nfo")) {
                        downdir = new File(MediaPath+"document\\Received");
                        file = new File(MediaPath+"document\\Received",filen);
                    } else {
                        downdir = new File(MediaPath+"other\\Received");
                        file = new File(MediaPath+"other\\Received",filen);
                    }
                } else {
                    downdir = new File(mw.p.getDownloadDir());
                    file = new File(mw.p.getDownloadDir(),filen);
                }
            }

            //Create Dir if not already done.
            if(downdir.mkdirs()) Logger.printLog("nice mkdirs");
            downdir = null;

            RAFile = new RandomAccessFile(file, "rw");

            if (file.length() > 0 && file.exists() && file.isFile() && file.canRead()) {
                verifyResumeSize = (int)Math.min(file.length(), (double)8192);
                bytesReceived = file.length();
                CCM.setBytes(ccID, bytesReceived);
                resumeFrom = file.length() - verifyResumeSize;
                RAFile.seek(resumeFrom);
            } else {
                resumeFrom = 0;
                verifyResumeSize = 0;
                bytesReceived = 0;
                CCM.setBytes(ccID, bytesReceived);
            }

            CCM.UpdateStatus(ccID);
            if (supportsAdcGet) {

                  SendCmd("$ADCGET","file files.xml.bz2 0 -1");
                
            }   else
                SendCmd("$Get",filename+COMMAND_DOL_CHAR+(resumeFrom + 1));
        }
    }

    private void cmdDirection(String incdat) throws IOException {
        if(CCM.getDirection(ccID) == ClientConnectionManager.Unknown) {
            if(thelock.startsWith("EXTENDEDPROTOCOL"))
                SendCmd("$Supports","XmlBZList MiniSlots");
            if("Download".equals(incdat.substring(0,incdat.indexOf(COMMAND_SEP_CHAR)))) {
                CCM.setDirection(ccID, ClientConnectionManager.Upload);
                SendCmd("$Direction", "Upload 300");
            } else {
                CCM.setDirection(ccID, ClientConnectionManager.Download);
                SendCmd("$Direction", "Download 300");
            }
            SendCmd("$Key",thekey);
        }
        if(CCM.getDirection(ccID) == ClientConnectionManager.Upload) {
            CCM.slotstaken += 1;
            CCM.setState(ccID, ClientConnectionManager.ULoad);
        } else
            CCM.setState(ccID, ClientConnectionManager.DLoad);
    }

    private void cmdGet(String incdat) throws IOException {
        int sepindex;
        CCM.setDirection(ccID, ClientConnectionManager.Upload);
        if(Integer.parseInt(mw.p.getUlSlots()) < CCM.slotstaken) {
            SendCmd("$MaxedOut","");
            CCM.setAbort(ccID, "No Free Slots to User");
            disconnect();
        } else {
            sepindex = incdat.indexOf(DIRPATH_SEP_CHAR);
            if(sepindex < 0) {
                filename = incdat.substring(0,incdat.lastIndexOf(COMMAND_DOL_CHAR));
                filen = filename;
            } else {
                filename = incdat.substring(sepindex+1, incdat.lastIndexOf(COMMAND_DOL_CHAR));
                sepindex = filename.indexOf(DIRPATH_SEP_CHAR);
                if(sepindex < 0)	filen = filename;
                else				filen = filename.substring(filename.lastIndexOf(DIRPATH_SEP_CHAR)+1);
            }

            CCM.setFile(ccID, filename);

            long fileoffset = Long.parseLong(incdat.substring(incdat.lastIndexOf(COMMAND_DOL_CHAR)+1, incdat.length()));

            if(filename.equals("MyList.DcLst") || filename.equals("files.xml.bz2")) {
                file = new File(filename);
            } else file = new File("./",filename);

            if(!file.exists()) {
                SendCmd("$Error", "File not available.");
                CCM.setAbort(ccID, "File Not Available to User");
                disconnect();
                return;
            }
            RAFile = new RandomAccessFile(file, "rw");
            RAFile.seek(fileoffset - 1);
            bytesReceived = fileoffset - 1;
            CCM.setBytes(ccID, bytesReceived);
            fileLength = file.length();
            CCM.setMaxBytes(ccID, fileLength);
            SendCmd("$FileLength", Long.toString(fileLength));
        }
    }

    private void cmdLock(String in, String incdat) throws IOException {
        thelock = incdat.substring(0,in.indexOf(" Pk=")-6);
        if(incdat.indexOf(" Pk=indexer") > 0)
            isMDCuser = true;
        thekey = encryptionHandler.calculateValidationKey(thelock);
        if(isSocketServer) {
            SendCmd("$MyNick", mw.p.getMynick());
            SendCmd("$Lock", "EXTENDEDPROTOCOLABCABCABCABCABCABC Pk=mDC"+ DCClient.version +"ABCABC");
        }

        if(CCM.RevConnectDLlist.containsKey(CCM.getNick(ccID))) {
            CCM.setDirection(ccID, ClientConnectionManager.Download);
            CCM.setFile(ccID, (String)CCM.RevConnectDLlist.get(CCM.getNick(ccID)));
            CCM.RevConnectDLlist.remove(CCM.getNick(ccID));
            }
        CCM.UpdateStatus(ccID);

        if(CCM.getDirection(ccID) > 0) {
            if(thelock.startsWith("EXTENDEDPROTOCOL"))
                SendCmd("$Supports","XmlBZList MiniSlots");
            if(CCM.getDirection(ccID) == ClientConnectionManager.Download)
                SendCmd("$Direction", "Download 300");
            else SendCmd("$Direction", "Upload 300");
            SendCmd("$Key",thekey);
        }
    }


    //::Send data to socket
	private void SendCmd(String command, String out) throws IOException
	{
		if(out.compareTo("") != 0)
			out = COMMAND_SEP_CHAR + out;
		Logger.printP2PLog("CLIENT OUT: "+command+out+COMMAND_END_CHAR);
		try {
			outdata.write((command+out+COMMAND_END_CHAR).getBytes("ISO-8859-1"));
			outdata.flush();
			mw.ULtraffic += command.length()+out.length()+1;
		} catch(SocketException e) {
			Logger.printLog("CLIENT OUT: KRASH:"+e);
			disconnect();
        }
	}


    //::Disconnect (violently)
    public void disconnect()
    {
        if (outdata != null) {
            try {
                outdata.close();

            } catch (Exception e) {
               e.printStackTrace();
            }
        }
        if (serv_sock != null) {
            try {
                if(isSocketServer) serv_sock.close();
            } catch (Exception e) {
                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
            }

            serv_sock = null;
        }

        if (sock!=null) {
            try {
                sock.close();
            } catch (Exception e) {
                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
            }
            sock = null;
        }

        if (RAFile!=null) {
            try {
                RAFile.close();
                RAFile = null;

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        if (file != null) {
            try {
                if (file.length() == 0) {

                    Logger.printGlobalLog("Zero file removed.");
                    file.delete();

                } else if (file.length() == fileLength) {

                    Logger.printGlobalLog("Downloaded: " + fileLength);
                    File completedir = new File("FileListComplete");
                    completedir.mkdirs();
                    File newfile = new File("FileListComplete",filen);
                    if (newfile.exists()) {

                        newfile.delete();
                    }

                    file.renameTo(newfile);

                    setUserCompleteState(STATE_SUCCESSED);
                }
                filename = null;
                file = null;

            }   catch (Exception e) {
                  e.printStackTrace();
            }
        }


        Logger.printGlobalLog("CLIENT: Disconnecting");
        if(CCM.getDirection(ccID) == ClientConnectionManager.Upload)
			CCM.slotstaken -= 1;
        toStop = true;

        if(CCM.getState(ccID) != ClientConnectionManager.Waita && CCM.getState(ccID) != ClientConnectionManager.Abort &&
        	CCM.getState(ccID) != ClientConnectionManager.Not_C && CCM.getState(ccID) != ClientConnectionManager.Finit) {
        	CCM.setState(ccID, ClientConnectionManager.Not_C);
        	CCM.UpdateStatus(ccID);
        	}
        CCM.setState(ccID, ClientConnectionManager.Not_C);

        //Release port
         mw.pp.unlockPort(port);

    }

    private void setUserCompleteState(int state) {
    	
    	mw.dsch.setUserState(CCM.getNick(ccID), state);
    	
    }
}